﻿using System;
using System.IO;
using System.Security.Cryptography;

namespace ooWii.Security
{
	public class PublicKey
	{
		#region PublicKey

		RSAEngine rsaEngine;
		SHA1Engine sha1Engine;
		public KeyType KeyType;
		public string Identity;
		public byte[] Modulus;
		public byte[] Exponent;
		public byte[] Bytes;
		public string Issuer;
		public Signature Signature;

		public static PublicKey Create( RSAEngine rsaEngine, SHA1Engine sha1Engine, Stream stream )
		{
			PublicKey publicKey = new PublicKey( rsaEngine, sha1Engine );

			publicKey.Signature = Signature.Create( stream );

			byte[] keyTypeBuffer = new byte[ 4 ];

			long position = stream.Position;
			stream.Position = position + 64;
			if( stream.Read( keyTypeBuffer, 0, keyTypeBuffer.Length ) != 4 )
			{
				throw new WiiException( "error reading public key type" );
			}

			KeyType keyType = (KeyType)WiiBuffer.GetUInt32( keyTypeBuffer, 0 );
			int keyLength = getKeyLength( keyType );

			int bytesLength = WiiBuffer.Align( 140 + keyLength, 64 );

			byte[] buffer = new byte[ bytesLength ];

			stream.Position = position;
			if( stream.Read( buffer, 0, buffer.Length ) != buffer.Length )
			{
				throw new WiiException( "error reading public key" );
			}

			publicKey.Bytes = buffer;
			publicKey.Issuer = WiiBuffer.GetString( buffer, 0, 64 );
			// skip 4 bytes for key type
			publicKey.Identity = publicKey.Issuer + "-" + WiiBuffer.GetString( buffer, 68, 64 );
			// skip 4 bytes
			publicKey.Modulus = WiiBuffer.GetBytes( buffer, 136, keyLength );
			publicKey.Exponent = WiiBuffer.GetBytes( buffer, 136 + keyLength, 4 );

			return publicKey;
		}

		public static int getKeyLength( KeyType keyType )
		{
			switch( keyType )
			{
			case KeyType.RSA4096:
				return 0x200;

			case KeyType.RSA2048:
				return 0x100;

			case KeyType.ElipticCurve:
				return 0x40;

			default:
				throw new WiiException( string.Format( "Invalid KeyType {0}", keyType ) );
			}
		}

		protected PublicKey( RSAEngine rsaEngine, SHA1Engine sha1Engine )
		{
			this.rsaEngine = rsaEngine;
			this.sha1Engine = sha1Engine;
		}

		public bool VerifySignature( byte[] buffer, Signature signature )
		{
			switch( KeyType )
			{
			case KeyType.RSA4096:
				return verifyRsaSignature( rsaEngine.RSA4096, buffer, signature );

			case KeyType.RSA2048:
				return verifyRsaSignature( rsaEngine.RSA2048, buffer, signature );

			case KeyType.ElipticCurve:
				throw new NotSupportedException();
			}

			throw new WiiException( string.Format( "Invalid KeyType {0}", KeyType ) );
		}

		bool verifyRsaSignature( RSACryptoServiceProvider rsa, byte[] buffer, Signature signature )
		{
			byte[] digest = sha1Engine.SHA1.ComputeHash( buffer, 0, buffer.Length );

			RSAParameters p = new RSAParameters();
			p.Modulus = Modulus;
			p.Exponent = Exponent;

			rsa.ImportParameters( p );

			return rsa.VerifyHash( digest, null, signature.Bytes );
		}

		#endregion
	}
}
